package com.dhl.fin.api.service;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.dhl.fin.api.common.util.FilesUtil;
import com.dhl.fin.api.common.util.JsonUtil;
import com.dhl.fin.api.common.util.MapUtil;
import com.dhl.fin.api.common.util.SpringContextUtil;
import com.dhl.fin.api.config.WeChatConfig;
import com.dhl.fin.api.dto.WeChatPayRiseResponseDTO;
import com.dhl.fin.api.dto.WeiXinRefundDTO;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.*;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Map;


@Slf4j
@Service
public class WeChatServiceImpl {

    private static final int QR_CODE_SIZE = 300; // 二维码的宽高

    private static final String REFUND_ORDER_PRE = "R_"; // 退款单号前缀

    private static final String MONEY_UNIT = "CNY"; // 币种

    private static final Integer QRCODE_LENGTH = 8; // 二维码文件随机长度

    private static final String QRCODE_SUB_PATH = "wxQrcode"; // 二维码子路径

    private NativePayService payService;

    private RefundService refundService;

    private WeChatConfig weChatConfig;

    @Autowired
    private OSSServiceImpl ossService;

    @Value("${custom.uploadPath}")
    private String localPath;

    {
        weChatConfig = SpringContextUtil.getBean(WeChatConfig.class);
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(weChatConfig.getMerchantId())
                        .privateKeyFromPath(weChatConfig.getPrivateKeyPath())
                        .merchantSerialNumber(weChatConfig.getMerchantSerialNumber())
                        .apiV3Key(weChatConfig.getApiV3key())
                        .build();
        payService = new NativePayService.Builder().config(config).build();

        refundService = new RefundService.Builder().config(config).build();


    }


    /**
     * 支付预下单
     *
     * @return
     */
    public WeChatPayRiseResponseDTO riseOrder(Integer prePrice, String description, String outTradeNo, String expireDateTime, String callBackUrl) throws IOException, WriterException, WriterException {

        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(prePrice);
        request.setAmount(amount);
        request.setAppid(weChatConfig.getAppId());
        request.setMchid(weChatConfig.getMerchantId());
        request.setDescription(description);
        request.setNotifyUrl(callBackUrl);
        request.setOutTradeNo(outTradeNo);

        //订单失效时间  微信默认支付时效时间2h
//        if (StringUtil.isNotEmpty(expireDateTime)) {
//            request.setTimeExpire(expireDateTime);
//        }

        // 调用微信支付，创建支付订单
        PrepayResponse response = payService.prepay(request);

        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.encode(response.getCodeUrl(), BarcodeFormat.QR_CODE, QR_CODE_SIZE, QR_CODE_SIZE);
        String qrcodeFileName = "qrcode_" + RandomUtil.randomNumbers(QRCODE_LENGTH) + ".png";
        FilesUtil.createDir(localPath + File.separator + QRCODE_SUB_PATH);
        String filePath = localPath + File.separator + QRCODE_SUB_PATH + File.separator + qrcodeFileName;
        Path file = FileSystems.getDefault().getPath(filePath);
        MatrixToImageWriter.writeToPath(bitMatrix, "PNG", file);

        String fileUrl = ossService.uploadToAliOss(qrcodeFileName, QRCODE_SUB_PATH, new FileInputStream(FilesUtil.getFile(filePath)));
        log.info("二维码已生成：" + fileUrl);

        return WeChatPayRiseResponseDTO.builder()
                .codeUrl(fileUrl)
                .orderNumber(outTradeNo)
                .build();

    }

    /**
     * 关闭订单
     *
     * @param orderNumber
     */
    public void closeOrder(String orderNumber) {
        CloseOrderRequest closeRequest = new CloseOrderRequest();
        closeRequest.setMchid(weChatConfig.getMerchantId());
        closeRequest.setOutTradeNo(orderNumber);
        payService.closeOrder(closeRequest);
    }

    /**
     * 退款明细
     */
    public void refundOrder(WeiXinRefundDTO refundDTO) {

        String refundOrderNumber = REFUND_ORDER_PRE + IdUtil.randomUUID();
        CreateRequest request = new CreateRequest();
        AmountReq amountReq = new AmountReq();
        amountReq.setRefund(refundDTO.getAmount());
        amountReq.setCurrency(MONEY_UNIT);
        amountReq.setTotal(refundDTO.getAmount());

        request.setAmount(amountReq);
        request.setReason(refundDTO.getReason());
        request.setTransactionId(refundDTO.getTransaction());
        request.setNotifyUrl(weChatConfig.getRefundCallBackUrl());
        request.setOutRefundNo(refundOrderNumber);
        request.setOutTradeNo(refundDTO.getOrderNumber());

        //发起退款
        refundService.create(request);

    }

    /**
     * 按照订单号查询订单状态
     */
    public Map queryStatusByOrderNumber(String outTradeNo) {
        QueryOrderByOutTradeNoRequest byOutTradeNoRequest = new QueryOrderByOutTradeNoRequest();
        byOutTradeNoRequest.setMchid(weChatConfig.getMerchantId());
        byOutTradeNoRequest.setOutTradeNo(outTradeNo);
        Transaction transaction = payService.queryOrderByOutTradeNo(byOutTradeNoRequest);
        return MapUtil.builder()
                .add("transactionId", transaction.getTransactionId(), "")
                .add("status", transaction.getTradeState().name())
                .build();
    }

    /**
     * 解密微信回调信息
     */
    public Map decryptCiphertext(String ciphertext, String nonce, String associatedData) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
        // 初始化Cipher对象，使用AES/GCM/NoPadding模式
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        // 将APIv3密钥转换成密钥对象
        SecretKeySpec keySpec = new SecretKeySpec(weChatConfig.getApiV3key().getBytes("UTF-8"), "AES");
        // 创建GCM参数对象
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, nonce.getBytes("UTF-8"));
        // 初始化Cipher为解密模式
        cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
        // 设置附加认证数据
        cipher.updateAAD(associatedData.getBytes("UTF-8"));
        // Base64解码加密数据
        byte[] encryptedDataBytes = Base64.getDecoder().decode(ciphertext);
        // 执行解密操作
        byte[] decryptedBytes = cipher.doFinal(encryptedDataBytes);
        // 将解密后的字节数组转换成字符串
        return JsonUtil.parseToMap(new String(decryptedBytes, "UTF-8"));
    }

}



